#!/usr/bin/env python3
"""Do a sanity check that you're at something that's ready for you to
cut an RC from; that you're in the right repo, that you're on the
right branch, that all of the subtrees are up-to-date...
"""

from typing import Generator

import sys
import time

import os.path

from os import getenv
from contextlib import contextmanager

from lib import assert_eq, git_check_clean, parse_bool, vX, vY, re_ga, re_ea
from lib.uiutil import Checker, CheckResult, run
from lib.uiutil import run_txtcapture as run_capture


DEFAULT_REPO = 'git@github.com:emissary-ingress/emissary'


def main(next_ver: str, quiet: bool = False, allow_untracked: bool = False) -> int:
    print(f'Starting work on "v{next_ver}"...')
    print()
    remote_repo = getenv('AMBASSADOR_RELEASE_REPO_OVERRIDE')
    if remote_repo is None or remote_repo == '':
        remote_repo = DEFAULT_REPO

    checker = Checker()

    @contextmanager
    def check(name: str) -> Generator[CheckResult, None, None]:
        with checker.check(name) as subcheck:
            # time.sleep(1)  # it's stupid, but honestly the delay makes the output more readable
            yield subcheck

    is_private = False

    with check(f"You're in a clone of {remote_repo}"):
        url = run_capture(['git', 'remote', 'get-url', '--push', 'origin'])
        if url.endswith("/"):
            url = url[:-len("/")]
        if url.endswith(".git"):
            url = url[:-len(".git")]
        if url.endswith("-private"):
            is_private = True
            url = url[:-len("-private")]
        assert_eq(url, remote_repo)

    with check("You're in the toplevel of the clone"):
        toplevel = run_capture(['git', 'rev-parse', '--show-toplevel'])
        if not os.path.samefile(toplevel, '.'):
            raise Exception(f"Not in {toplevel}")

    with check("You're in a clean checkout"):
        git_check_clean(allow_untracked=allow_untracked)

    # Cache the name of our remote...
    remote_name = f'{remote_repo}.git' if is_private else 'origin'

    # ...make sure the passed-in version is OK...
    m = re_ga.match(next_ver)
    if not m:
        m = re_ea.match(next_ver)
    assert m

    # ...and figure out some branch names.
    release_branch = os.environ.get("RELEASE_BRANCH") or f"release/v{m[vX]}.{m[vY]}"
    cur_branch = ""

    with check(f"You're on master or {release_branch}"):
        cur_branch = run_capture(['git', 'rev-parse', '--abbrev-ref', 'HEAD'])

        if (cur_branch != "master") and (cur_branch != release_branch):
            raise AssertionError(f"You can't start a release from {cur_branch}")

    if checker.ok:
        with check("You're up-to-date with ambassador.git"):
            remote_name = f'{remote_repo}.git' if is_private else 'origin'

            branch_up_to_date(
                remote=remote_name,
                branch=cur_branch,
                update_cmd=f'git pull {remote_name} {cur_branch}',
            )

        if is_private:
            with check("You're up-to-date with ambassador-private.git"):
                branch_up_to_date(
                    remote='f{remote_repo}-private.git',
                    branch=cur_branch,
                    update_cmd=f'git pull {remote_repo}-private.git {cur_branch}',
                )

    if checker.ok:
        if not quiet:
            print()
            print("Yep, looks like you're good to proceed to running `start-update-version`.")
        return 0
    else:
        print()
        print("Looks like there's something wrong with your tree that you need to address before continuing.")
        return 1


def branch_exists(remote: str, branch: str) -> None:
    # Allow exceptions to propagate upward
    run(['git', 'fetch', remote, f'refs/heads/{branch}'])


def branch_up_to_date(remote: str, branch: str, update_cmd: str) -> None:
    run(['git', 'fetch', remote, f'refs/heads/{branch}'])
    try:
        run(['git', 'merge-base', '--is-ancestor', 'FETCH_HEAD', 'HEAD'])
    except Exception as err:
        print(f"HEAD is not up-to-date with '{remote}' '{branch}':")
        print("You need to update it with:")
        print()
        print(f"    $ {update_cmd}")
        print()
        raise


subtree_up_to_date = branch_up_to_date

if __name__ == '__main__':
    args = sys.argv[1:]
    quiet = False

    allow_untracked = parse_bool(getenv("ALLOW_UNTRACKED", "false"))

    if args and (args[0] == '--quiet'):
        quiet = True
        args.pop(0)

    if len(args) != 1 or (not re_ga.match(args[0]) and not re_ea.match(args[0])):
        sys.stderr.write(f"{os.path.basename(sys.argv[0])} Version must match X.Y.Z(-ea)\n")
        sys.exit(2)

    sys.exit(main(next_ver=args[0], quiet=quiet, allow_untracked=allow_untracked))
